package com.heima.article.service.impl;

import com.heima.article.entity.ApArticle;
import com.heima.article.entity.ApHotArticles;
import com.heima.article.mapper.ApHotArticlesMapper;
import com.heima.article.service.ApArticleService;
import com.heima.article.service.ApHotArticlesService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.common.util.JsonUtils;
import com.heima.model.article.dtos.ArticleBehaviorStreamMsg;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.List;

/**
 * <p>
 * 热文章表 服务实现类
 * </p>
 *
 * @author HM
 * @since 2023-05-25
 */
@Slf4j
@Service
public class ApHotArticlesServiceImpl extends ServiceImpl<ApHotArticlesMapper, ApHotArticles> implements ApHotArticlesService {

    public static final String PREFIX = "leadnews:hotArticles";

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private ApArticleService articleService;

    @Override
    public void updateArticleScore(ArticleBehaviorStreamMsg msg) {
        // 更新redis中zset的分值
        double score = calcArticleScore(msg);
        redisTemplate.boundZSetOps(PREFIX).incrementScore(""+msg.getArticleId(), score);
        // 把行为的详情数据记录到redis中，将来凌晨使用定任务把详细数据写入到mysql
        // 先记录一个要同步所有的article的Id的列表，为了将来做定时任务数据更新的时候，避免全库扫描redis
        redisTemplate.boundSetOps(PREFIX + ":set").add(""+msg.getArticleId());
        // 记录每一个article的行为数据的详情
        BoundValueOperations<String, String> stringOpt = redisTemplate.boundValueOps(PREFIX + ":behavior:" + msg.getArticleId());
        String oldData = stringOpt.get();
        if(StringUtils.isEmpty(oldData)){
            stringOpt.set(JsonUtils.toString(msg));
        }else {
            String data = sum(oldData, msg);
            stringOpt.set(JsonUtils.toString(data));
        }
        // 当凌晨的时候，开启定时任务，读取leadnews:hot_article:set中的所有的articleId
        // 遍历每一个articleId，读取"leadnews:hot_article:" + articleId，这里面就有行为的详细数据
        // 把这些行为的基础数据更新到mysql中
    }

    private String sum(String oldData, ArticleBehaviorStreamMsg msg){
        ArticleBehaviorStreamMsg oldMsg = JsonUtils.toBean(oldData, ArticleBehaviorStreamMsg.class);
        ArticleBehaviorStreamMsg newMsg = new ArticleBehaviorStreamMsg();
        newMsg.setArticleId(msg.getArticleId());
        newMsg.setViews(add(oldMsg.getViews(), msg.getViews()));
        newMsg.setLikes(add(oldMsg.getLikes() , msg.getLikes()));
        newMsg.setComment(add(oldMsg.getComment(), msg.getComment()));
        newMsg.setCollection(add(oldMsg.getCollection(), msg.getCollection()));
        return JsonUtils.toString(newMsg);
    }

    private Integer add(Integer a, Integer b){
        if(a == null){
            a = 0;
        }
        if(b == null){
            b = 0;
        }
        return a+b;
    }

    @Override
    public void calcArticleScore() {
        // 1 查询最近5天发布的文章, 请改成分页，可以使用分片任务
        List<ApArticle> articleList = articleService.findLatestArticlesIn(5);
        // 2 计算文章的热度值
        if(CollectionUtils.isEmpty(articleList)){
            log.warn("5天内一个文章也查不到");
            return;
        }
        for(ApArticle article : articleList){
            double score = calcArticleScore(article);
            // 3 把热度值写入到redis的zset
            redisTemplate.boundZSetOps(PREFIX).add(""+article.getId(), score);
            // 当article上下架的时候，redis中需要同步做上下架，接收消息，同步上下架就可以 TODO
            redisTemplate.boundValueOps(PREFIX+":"+article.getId()).set(JsonUtils.toString(article));
        }
    }

    private double calcArticleScore(ArticleBehaviorStreamMsg msg){
        ApArticle article = new ApArticle();
        article.setViews(Long.parseLong(msg.getViews()==null?"0":msg.getViews().toString()));
        article.setLikes(Long.parseLong(msg.getLikes()==null?"0":msg.getLikes().toString()));
        article.setCollection(Long.parseLong(msg.getCollection()==null?"0":msg.getCollection().toString()));
        article.setComments(Long.parseLong(msg.getComment()==null?"0":msg.getComment().toString()));
        return calcArticleScore(article);
    }

    private double calcArticleScore(ApArticle article){
        double score =  0;
        Long views = article.getViews();
        Long likes = article.getLikes();
        Long comments = article.getComments();
        Long collection = article.getCollection();
        score += views;
        score += 3*likes;
        score += 5*comments;
        score += 8*collection;
        return score;
    }

}
